package net.betou.common.hibernate3;

import static org.hibernate.EntityMode.POJO;

import java.io.Serializable;
import java.util.List;

import net.betou.common.page.Pagination;
import net.betou.common.util.MyBeanUtils;

import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.util.Assert;

/**
 * hibernate DAO基类
 * 
 * 提供hql分页查询，拷贝更新等一些常用功能。
 * 
 * @author 叶装装
 * 
 * @param <T>
 *            entity class
 * @param <ID>
 *            entity id
 */
public abstract class HibernateBaseDao< T, ID extends Serializable > extends HibernateSimpleDao{
	/**
	 * @see Session.get(Class,Serializable)
	 * @param id
	 * @return 持久化对象
	 */
	protected T get( ID id ) {
		return get( id, false );
	}

	/**
	 * @see Session.get(Class,Serializable,LockMode)
	 * @param id
	 *            对象ID
	 * @param lock
	 *            是否锁定，使用LockMode.UPGRADE
	 * @return 持久化对象
	 */
	@SuppressWarnings( "unchecked" )
	protected T get( ID id, boolean lock ) {
		T entity;
		if( lock ){
			entity = ( T ) getSession( ).get( getEntityClass( ), id, LockMode.UPGRADE );
		}else{
			entity = ( T ) getSession( ).get( getEntityClass( ), id );
		}
		return entity;
	}

	/**
	 * 按属性查找对象列表
	 */
	@SuppressWarnings( "unchecked" )
	protected List< T > findByProperty( String property, Object value ) {
		Assert.hasText( property );
		return createCriteria( Restrictions.eq( property, value ) ).list( );
	}

	/**
	 * 按属性查找唯一对象
	 */
	@SuppressWarnings( "unchecked" )
	protected T findUniqueByProperty( String property, Object value ) {
		Assert.hasText( property );
		Assert.notNull( value );
		return ( T ) createCriteria( Restrictions.eq( property, value ) ).uniqueResult( );
	}

	/**
	 * 按属性统计记录数
	 * 
	 * @param property
	 * @param value
	 * @return
	 */
	protected int countByProperty( String property, Object value ) {
		Assert.hasText( property );
		Assert.notNull( value );
		return ( ( Number ) ( createCriteria( Restrictions.eq( property, value ) ).setProjection( Projections.rowCount( ) ).uniqueResult( ) ) ).intValue( );
	}

	/**
	 * 按Criterion查询列表数据.
	 * 
	 * @param criterion
	 *            数量可变的Criterion.
	 */
	@SuppressWarnings( "unchecked" )
	protected List findByCriteria( Criterion ... criterion ) {
		return createCriteria( criterion ).list( );
	}

	/**
	 * 通过Updater更新对象
	 * 
	 * @param updater
	 * @return
	 */
	@SuppressWarnings( "unchecked" )
	public T updateByUpdater( Updater< T > updater ) {
		ClassMetadata cm = sessionFactory.getClassMetadata( getEntityClass( ) );
		T bean = updater.getBean( );
		T po = ( T ) getSession( ).get( getEntityClass( ), cm.getIdentifier( bean, POJO ) );
		updaterCopyToPersistentObject( updater, po, cm );
		return po;
	}

	/**
	 * 将更新对象拷贝至实体对象，并处理many-to-one的更新。
	 * 
	 * @param updater
	 * @param po
	 */
	private void updaterCopyToPersistentObject( Updater< T > updater, T po, ClassMetadata cm ) {
		String [ ] propNames = cm.getPropertyNames( );
		String identifierName = cm.getIdentifierPropertyName( );
		T bean = updater.getBean( );
		Object value;
		for ( String propName : propNames ){
			if( propName.equals( identifierName ) ){
				continue;
			}
			try{
				value = MyBeanUtils.getSimpleProperty( bean, propName );
				if( !updater.isUpdate( propName, value ) ){
					continue;
				}
				cm.setPropertyValue( po, propName, value, POJO );
			}catch( Exception e ){
				throw new RuntimeException( "copy property to persistent object failed: '" + propName + "'", e );
			}
		}
	}

	/**
	 * 根据Criterion条件创建Criteria,后续可进行更多处理,辅助函数.
	 */
	protected Criteria createCriteria( Criterion ... criterions ) {
		Criteria criteria = getSession( ).createCriteria( getEntityClass( ) );
		for ( Criterion c : criterions ){
			criteria.add( c );
		}
		return criteria;
	}
	
	/**
	 * 使用DetachedCriteria得到列表... 
	 * @param sql
	 * 					执行sql语句
	 * @return
	 */
	@SuppressWarnings( "unchecked" )
	protected List< T > findBySQL( String sql ){
		Criteria c = createCriteria( sql );
		
		return c.list( );
	}
	
	/**
	 * 使用SQL语句创建Criteria
	 * @param sql
	 * @return
	 */
	protected Criteria createCriteria( String sql ){
		DetachedCriteria detached = DetachedCriteria.forClass( getEntityClass( ) );
		detached.add( Restrictions.sqlRestriction( sql ) );
		Criteria c = detached.getExecutableCriteria( getSession() );
		return c;
	}

	/**
	 * 创建查询，根据Criteria
	 * @param searchProperty
	 * @param searchValue
	 * @param orderProperty
	 * @param orderDirection
	 * @param pageNo
	 * @param pageSize
	 * @param sql
	 * @return
	 */
	public Pagination getPageByCriteria( String searchProperty, String searchValue, String orderProperty, String orderDirection, int pageNo, int pageSize, String sql  ){
		Criteria c = this.createCriteria( sql ); // 创建对象
		createSearch( c, searchProperty, searchValue );
		createOrder( c, orderProperty, orderDirection, "id" );
		Pagination p = this.findByCriteria( c, pageNo, pageSize );
		return p;
	}
	
	
	/**
	 * 获得Dao对于的实体类
	 * 
	 * @return
	 */
	abstract protected Class< T > getEntityClass( );
}
